package com.dynamic.mybatis.core.metadata;


import com.baomidou.mybatisplus.core.MybatisConfiguration;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.dynamic.mybatis.core.intercept.DynamicMappedStatementHandler;
import com.dynamic.mybatis.core.toolkit.StringUtils;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMap;
import org.apache.ibatis.mapping.ResultMap;
import org.apache.ibatis.parsing.XNode;

import java.util.*;
import java.util.stream.Collectors;

public class DynamicConfiguration extends MybatisConfiguration{
    private static final Log logger = LogFactory.getLog(DynamicConfiguration.class);
    protected final Map<String, DynamicMappedStatement> dynamicMappedStatements =  new StrictMap<DynamicMappedStatement>("Dynamic Mapped Statement Attributes collection").conflictMessageProducer((savedValue, targetValue) -> ". please check " + savedValue.toString() + " and " + targetValue.toString());
    public  final String DYNAMIC_RESOURCE = "com.dynamic.mybatis.DynamicMappedStatement";

    Set<DynamicMappedStatementHandler> mappedStatementHandlers = new HashSet<>();
    Set<String> temporaryNamespaces = new HashSet<>();

    public void addTemporaryNamespace(String temporaryNamespace){
        if(StringUtils.isBlank(temporaryNamespace)){
            return;
        }
        this.temporaryNamespaces.add(temporaryNamespace);
    }

    public Collection values(){
       return this.dynamicMappedStatements.values();
    }

    public void cleanTemporaryNamespace(String temporaryNamespace){
        if(dynamicMappedStatements.size() < 1){ return; }

        List<DynamicMappedStatement> dmsList = new ArrayList<>();
        Iterator<Map.Entry<String, DynamicMappedStatement>> dmsIterator =  dynamicMappedStatements.entrySet().iterator();
        while (dmsIterator.hasNext()){
            Map.Entry entry = dmsIterator.next();
            Object value = entry.getValue();
            if(value instanceof DynamicMappedStatement &&  ((DynamicMappedStatement) value).getNamespace().equals(temporaryNamespace)){
                dmsList.add((DynamicMappedStatement)value);
            }
        }
        cleanDynamicMappedStatement(dmsList);
        //删除 sqlFragment
        Iterator<Map.Entry<String, XNode>> sqlFragmentIterator =  sqlFragments.entrySet().iterator();
        while (sqlFragmentIterator.hasNext()){
            Map.Entry entry = sqlFragmentIterator.next();
            Object value = entry.getValue();
            String key  = (String) entry.getKey();
            if(!(value instanceof XNode)|| !key.startsWith(temporaryNamespace) ){ continue;}
            //删除 DynamicMappedStatement
            sqlFragmentIterator.remove();
        }
    }

    public void cleanDynamicMappedStatement(List<DynamicMappedStatement> dmsList){
        if(CollectionUtils.isEmpty(dmsList)){ return; }
         dmsList.stream().forEach(dms ->{
            String key = dms.getId();
             //删除 DynamicMappedStatement
             dynamicMappedStatements.remove(key);
             MappedStatement ms =    getMappedStatement(key);
             if(ms == null){ return; }
             //删除 MappedStatement
             super.mappedStatements.remove(key);
             //删除 ParameterMap
             super.parameterMaps.remove(ms.getParameterMap().getId());
             //删除 ResultMap
             if(ms.getResultMaps() != null){
                 for(ResultMap rm : ms.getResultMaps()){
                     super.resultMaps.remove(rm.getId());
                 }
             }
        });
    }


    public void addMappedStatementHandler(DynamicMappedStatementHandler mappedStatementHandler){
        if(mappedStatementHandler == null ){return; }
        mappedStatementHandlers.add(mappedStatementHandler);
    }
    public void addMappedStatementHandler(Collection<DynamicMappedStatementHandler> mappedStatementHandlers){
        if(mappedStatementHandlers == null || mappedStatementHandlers.size() < 1){return;}
        for(DynamicMappedStatementHandler dmsi : mappedStatementHandlers){
            addMappedStatementHandler(dmsi);
        }
    }

    public Set<DynamicMappedStatementHandler> mappedStatementHandlers(){
         return this.mappedStatementHandlers;
    }

    public DynamicMappedStatement getDynamicMappedStatement(String mappedStatementId) {
       return  dynamicMappedStatements.containsKey(mappedStatementId) ? dynamicMappedStatements.get(mappedStatementId) : null;
    }

    public Map<String, DynamicMappedStatement> getDynamicMappedStatements() {
        return dynamicMappedStatements;
    }
    public Boolean hasMappedStatement(String id) {
        return  mappedStatements.containsKey(id);
    }

    public boolean isResourceLoaded(String resource) {
        if(!DYNAMIC_RESOURCE.equals(resource)){
            return loadedResources.contains(resource);
        }
        return false;
    }

    public void addDynamicMappedStatement(DynamicMappedStatement dms) {
        logger.debug("addDynamicMappedStatement: " + dms.getId());
        DynamicMappedStatement dmsNative =  getDynamicMappedStatement(dms.getId());
        if(dmsNative != null ){
            if(StringUtils.isNotEmpty(dmsNative.getUid()) && !Objects.equals(dmsNative.getUid(),dms.getUid())){
                throw new IllegalArgumentException(dmsNative.getId()+"[uid["+dmsNative.getUid()+"]]" + " already contains value for " +  dms.getId()+"[uid["+dms.getUid()+"]]");
            }else {
               logger.warn("addDynamicMappedStatement  replace existed. "+dms.getId());
               cleanDynamicMappedStatement(Arrays.asList(dmsNative));
            }
           }
        before(dms);
        dynamicMappedStatements.put(dms.getId(),dms);
        after(dms);
        if(StringUtils.isBlank(dms.getUid())){
            dms.setUid(UUID.randomUUID().toString());
        }
    }

    @Override
    public void addMappedStatement(MappedStatement ms) {
        logger.debug("addMappedStatement: " + ms.getId());
        if (mappedStatements.containsKey(ms.getId())) {
            logger.error("mapper[" + ms.getId() + "] is ignored, because it exists, maybe from xml file");
            mappedStatements.put(ms.getId(), ms);
        }
        super.addMappedStatement(ms);
    }

    @Override
    public void addParameterMap(ParameterMap pm) {
        logger.debug("addParameterMap: " + pm.getId());
        if (resultMaps.containsKey(pm.getId())) {
            logger.error("parameterMaps[" + pm.getId() + "] is ignored, because it exists, maybe from xml file");
        }
        super.addParameterMap(pm);
    }

    @Override
    public void addResultMap(ResultMap rm) {
        logger.debug("addResultMap: " + rm.getId());
        if (resultMaps.containsKey(rm.getId())) {
            logger.error("resultMap[" + rm.getId() + "] is ignored, because it exists, maybe from xml file");
        }
        super.addResultMap(rm);
    }

    public void addSqlFragments(Map<String,XNode> xNodes) {
        sqlFragments.putAll(xNodes);
    }

    public MappedStatement getMappedStatement(String id) {
       return  hasMappedStatement(id) ? getMappedStatement(id,true) : null;
    }


    private void before(DynamicMappedStatement dms){
        if(this.mappedStatementHandlers.size() > 0){
            DynamicMappedStatementHandler.OPERATE operate =  hasMappedStatement(dms.getId()) ? DynamicMappedStatementHandler.OPERATE.UPDATE  : DynamicMappedStatementHandler.OPERATE.ADD;
            for(DynamicMappedStatementHandler msi : this.mappedStatementHandlers){
                msi.before(operate,dms);
            }
        }
    }

    private void after(DynamicMappedStatement dms){
        if(this.mappedStatementHandlers.size() > 0){
            DynamicMappedStatementHandler.OPERATE operate =  hasMappedStatement(dms.getId()) ? DynamicMappedStatementHandler.OPERATE.UPDATE  : DynamicMappedStatementHandler.OPERATE.ADD;
            for(DynamicMappedStatementHandler msi : this.mappedStatementHandlers){
                msi.after(operate,dms);
            }
        }
    }
}
